#!/usr/bin/python

import os
from os.path import join
import pysvn
from pysvn import node_kind, ClientError
from tempfile import mkdtemp
from urlparse import urlparse

class Configurator:
    def __init__(self, base_url, role=None, default_to_base=False, rev_num=None, temp_base='/tmp/', username=None, password=None):

        self.base_url       = base_url
        self.role           = role
        self.default_to_base= default_to_base
        self.temp_base      = temp_base
        self.username       = username
        self.password       = password

        self.client         = pysvn.Client()
        self.client.callback_notify = self.notify
        self.client.callback_get_login = self.handleLogin
        self.client.callback_ssl_server_trust_prompt = self.handleTrustPrompt

        self.root_dir       = '/'
        self.files          = []
        self.dirs           = []

        self.new            = []
        self.changed        = []
        self.same           = []

        if rev_num:
            self.revision   = pysvn.Revision( pysvn.opt_revision_kind.number, rev_num)
            print "Revision: %i" % rev_num
        else:
            self.revision   = pysvn.Revision( pysvn.opt_revision_kind.head )
            print "Revision: HEAD"

        self.url =  self.getURL()
        print "Updating from [%s]" % self.url
        self.temp_dir = mkdtemp(dir=temp_base)
        self.client.checkout(self.url, self.temp_dir, revision=self.revision)

    def urlExists(self, url):
        try:
            self.client.info2(url, self.revision)
        except ClientError:
            return False
        return True

    def getURL(self):
        default_url = os.path.join(self.base_url, 'base')
        if not self.urlExists(default_url):
            raise Exception("FATAL: base path [%s] doesn't exist!" % default_url)
        url = None
        if self.role:
            url = os.path.join(self.base_url, 'roles' , self.role)
        else:
            try:
                info = self.client.info(self.root_dir)
                if info.url.startswith(self.base_url):
                    url = info.url
                    print "Using role from existing checkout:  %s" % url
                else:
                    url = default_url
            except ClientError:
                url = default_url

        if url==default_url or self.urlExists(url):
            return url
        elif self.default_to_base:
            print "WARNING: can't find path [%s], defaulting to [%s]" % (url,default_url)
            return default_url
        else: 
            raise Exception("Path [%s] does not exist at revision [%s]" % (url,self.revision))

    def notify(self, event):
        if event['action'] == pysvn.wc_notify_action.update_completed:
            self.revision = event['revision']

    def handleLogin(self, realm, username, may_save):
        return (False, self.username, self.password, False)

    def handleTrustPrompt(self, trust_dict):
        #FIXME Need to actually verify the cert here
        return(True, 0, False)

    def getManagedFiles(self):
        entries = self.client.ls(self.url, revision=self.revision, recurse=True)
        filepaths = []
        dirpaths  = [self.temp_dir]
        for entry in entries:
            path = entry['name'][len(self.url)+1:]
            if entry['kind'] == node_kind.dir:
                self.dirs.append(path)
            elif entry['kind'] == node_kind.file:
                self.files.append(path)
        return (self.files,self.dirs)

    def delTree(self,top):
        for base, dirs, files in os.walk(top, topdown=False):
            for name in files:
                os.remove(join(base,name))
            for name in dirs:
                path = join(base,name)
                if os.path.islink(path):
                    os.remove(path)
                else:
                    os.rmdir(path)
        os.rmdir(top)

    def diff(self,realpath, temppath):
        real_is_link = os.path.islink(realpath)
        temp_is_link = os.path.islink(temppath)
        if real_is_link or temp_is_link:
            if real_is_link != temp_is_link: 
                # one is a link, one isn't
                return True
            else:
                real_linkpath = os.readlink(realpath)
                temp_linkpath = os.readlink(temppath)
                return real_linkpath != temp_linkpath
        else: 
            # both are regular files
            return os.system('diff -q %s %s > /dev/null' % (realpath,temppath))

    def diffTree(self):
        new     = []
        changed = []
        same    = []
        for path in self.files:
            realpath = join(self.root_dir,path)
            svnpath  = join(self.temp_dir,path)

            if os.access(realpath,os.F_OK): 
                if self.diff(realpath,svnpath):
                    print "changed: %s" % realpath
                    self.changed.append(path)
                else:
                    self.same.append(path)
            else:
                self.new.append(path)
        return(self.new, self.changed, self.same)

    def writeConfig(self):
        return os.system('rsync -rlt %s/ %s' % (self.temp_dir, self.root_dir))

    def cleanup(self):
        self.delTree(self.temp_dir)
